﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Agreement;

namespace TempSpace
{
    class Program
    {

        static void Main(string[] args)
        {

            // First, we need some data to work with.
            byte[] Message;
            {
                System.Environment.CurrentDirectory = @"c:\users\kyanha.skee-lap-r2\work\sandbox";
                var s = new StreamsContainer();
                var count = 0;
                foreach (var i in System.IO.Directory.GetFiles(System.Environment.CurrentDirectory, @"cert*.pem"))
                {
                    count++;
                    s.AddStream(new FileStream(i, FileMode.Open));
                    if (count > 3)
                        break;
                }
                Message = s.AsDer();
                s.Dispose();
                System.Console.WriteLine(Message);
            }
            // we have Message, so let'CertParser hash it
            byte[] HashOutput = new byte[512 / 8];  // SHA-512
            {
                var hasher = new Org.BouncyCastle.Crypto.Digests.Sha512Digest();
                hasher.BlockUpdate(Message, 0, Message.Length);
                hasher.DoFinal(HashOutput, 0);
                Console.WriteLine(new DerOctetString(HashOutput).ToString());
            }
            // Hash complete.
            var sharedkey = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
            byte[] CryptOut;
            byte[] DecryptOut;
            {
                // Now, to encrypt Message.  We're not generating a new key, we're using a no-bit-set key.
                
                {
                    var symcryptengine = new Org.BouncyCastle.Crypto.Engines.AesEngine();
                    var bp = new Org.BouncyCastle.Crypto.Paddings.PaddedBufferedBlockCipher(symcryptengine);
                    CryptOut = new byte[bp.GetOutputSize(Message.Length)];

                    bp.Init(true, new Org.BouncyCastle.Crypto.Parameters.KeyParameter(sharedkey));
                    //byte[] symcrypttemp = new byte[CryptOut.Length];

                    //paddedblocklen = bp.ProcessBytes(Message, 0, Message.Length, symcrypttemp, 0)
                    //    + bp.GetBlockSize();
                    //symcrypttemp.CopyTo(CryptOut, 0);
                    //Console.WriteLine(new DerOctetString(symcrypttemp));
                    bp.DoFinal(Message, 0, Message.Length, CryptOut, 0);
                    Console.WriteLine(new DerOctetString(CryptOut));
                }
                {
                    var decryptengine = new Org.BouncyCastle.Crypto.Engines.AesEngine();
                    var decryptcipher = new Org.BouncyCastle.Crypto.Paddings.PaddedBufferedBlockCipher(decryptengine);
                    decryptcipher.Init(false, new Org.BouncyCastle.Crypto.Parameters.KeyParameter(sharedkey));
                    DecryptOut = new byte[decryptcipher.GetOutputSize(CryptOut.Length)];
                    //var symdecrypttemp = new byte[DecryptOut.Length];

                    //DecryptOut = decryptcipher.ProcessBytes(CryptOut);
                    //DecryptOut.CopyTo(DecryptOut, 0);

                    decryptcipher.DoFinal(CryptOut, 0, CryptOut.Length, DecryptOut, 0);
                    //System.Diagnostics.Debugger.Break();
                }
            }

            // Okay, now we've figured out how to symmetrically encrypt and decrypt.
            // Can we encrypt the hash with our private key to an arbitrary public key?

            {
                ECPrivateKeyParameters inprivatekey;
                ECPrivateKeyParameters outprivatekey;
                X509Certificate incert;
                X509Certificate outcert;
                {
                    var CertParser = new Org.BouncyCastle.X509.X509CertificateParser();

                    {
                        var incertstream = System.IO.File.OpenRead(@"cert[00,00].pem");
                        var inkeystream = System.IO.File.OpenRead(@"key[00,00].pem");
                        using (TextReader textReader = new StreamReader(inkeystream))
                        {
                            incert = CertParser.ReadCertificate(incertstream);
                            incertstream.Close();
                            var inreader = new Org.BouncyCastle.OpenSsl.PemReader(textReader);
                            inprivatekey = inreader.ReadObject() as ECPrivateKeyParameters;
                            inkeystream.Close();
                            if (null == inprivatekey)
                                throw new CryptoException("failed to read private key 1");
                        }
                    }
                    {
                        var outcertstream = System.IO.File.OpenRead(@"cert[00,01].pem");
                        var outkeystream = System.IO.File.OpenRead(@"key[00,00].pem");

                        using (TextReader textReader = new StreamReader(outkeystream))
                        {
                            outcert = CertParser.ReadCertificate(outcertstream);
                            outcertstream.Close();
                            var outreader = new Org.BouncyCastle.OpenSsl.PemReader(textReader);
                            outprivatekey = outreader.ReadObject() as ECPrivateKeyParameters;
                            outkeystream.Close();
                            if (null == outprivatekey)
                                throw new CryptoException("failed to read private key 2");
                        }
                    }
                }
                // At this moment, we have the in (#1) private key/cert and the out (#2) of the same.

                
                // We need to now encrypt the hash, by using 1's private/2's public key to derive DH
                // and use that to encrypt the HashOutput.  (We need another AES implementation here.)
                
                var ecdhbaout = new Org.BouncyCastle.Crypto.Agreement.ECDHCBasicAgreement();
                var ecdhbain = new Org.BouncyCastle.Crypto.Agreement.ECDHCBasicAgreement();

                // It appears that we need an ICipherParameters, which means we need a
                // KeyParameter.  Preferably if we can come up with a ParametersWithRandom
                // it'll make things easier.

                ecdhbain.Init(inprivatekey);
                var A = ecdhbain.CalculateAgreement(outcert.GetPublicKey());

                ecdhbaout.Init(outprivatekey);
                var B = ecdhbaout.CalculateAgreement(incert.GetPublicKey());

                if (A.ToString() == B.ToString())
                    Console.WriteLine("ECDH operations match");
                else
                    Console.WriteLine("ECDH operations DO NOT MATCH");
                System.Diagnostics.Debugger.Break();

            }

            Console.WriteLine("Here's the breakpoint");

            //var t = new Asn1InputStream(Message);
            //Asn1Object n;
            //do
            //{
            //    n = t.ReadObject();
            //    if (n == null)
            //        break;
            //    Asn1Sequence textReader = n as Asn1Sequence;
            //    foreach (var x in textReader) {
            //        var v = new DerOctetString(x as Asn1Encodable);
            //        Console.WriteLine(v.ToString());
            //    }
            //    Console.ReadKey();
            //} while (true);
            Console.ReadKey();
        }
    }
}
